package sli.sli2.pcp.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author Group 8, Khassraf Roman, Mueller Robin, Zischka Stefan
 *
 */
public class TCPRequestService {
    private LoggingService loggingService;
    private AuthorizationService authService;
    private static final Logger log = LoggerFactory.getLogger(TCPRequestService.class);
    private int seqNr = -1;
    
    public TCPRequestService(LoggingService loggingService) {
        this.loggingService = loggingService;
        this.authService = new AuthorizationService(loggingService);
    }
    
    public String respond(String tcpInput) {
        
        if (tcpInput.startsWith("!check")) {
            return authCmd(tcpInput);
        } else if (tcpInput.startsWith("!logEvent")) {
            return logCmd(tcpInput);
        }
        log.info("received unknown command {}", tcpInput);
        return "error unknown command";
    }
    
    private String authCmd(String cmd) {
        log.info("Received check command");
        String[] c = cmd.split(" ");
        if (c.length < 5) {
            log.info("Check command has insufficient argument number");
            return "error missing arguments";
        }
        
        String authHash = null;
        int zoneId = -1;
        int terminalId = -1;
        int pin = -1;
        String bioHash = null;
        int cSeqNr = -1;
        
        for (int i=1; i<c.length; i++) {
            String[] pair = c[i].split(":");
            if (pair.length != 2) {
                log.info("Check command has invalid syntax {}", c[i]);
                return "error argument syntax seqNr:" + String.valueOf(++this.seqNr);
            }
            switch(pair[0]) {
                case "auth":
                    authHash = pair[1];
                    break;
                case "zone":
                    try {
                        zoneId = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("Check command has invalid zone {}", pair[1]);
                        return "error invalid zoneId seqNr:" + String.valueOf(++this.seqNr);
                    }
                    break;
                case "terminal":
                	try {
                		terminalId = Integer.parseInt(pair[1]);
                	} catch (NumberFormatException ex) {
                		log.info("Check command has invalid terminal {}", pair[1]);
                		return "error invalid terminalId seqNr:" + String.valueOf(++this.seqNr);
                	}
                    break;
                case "pin":
                    try {
                        pin = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("Check command has invalid pin {}", pair[1]);
                        return "error invalid pin seqNr:" + String.valueOf(++this.seqNr);
                    }
                    break;
                case "biometrichash":
                    bioHash = pair[1];
                    break;
                case "seqNr":
                    try {
                        cSeqNr = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("Check command has invalid seqNr {}", pair[1]);
                        loggingService.addLogEntryAndNotify(0, 0, "PCP received invalid sequence number", "technical staff", "message");
                        return "error invalid sequenceNumber seqNr:0";
                    }
                    if (this.seqNr == -1) {
                        this.seqNr = cSeqNr;
                    } else if (cSeqNr <= this.seqNr) {
                        log.info("Check command has invalid seqNr {}", pair[1]);
                        loggingService.addLogEntryAndNotify(0, 0, "PCP received invalid sequence number", "technical staff", "message");
                        return "error invalid sequenceNumber seqNr:0";
                    }
                    break;
                default: 
                    break;
            }
        }
        
        if (cSeqNr == -1) {
            log.info("Check command does not provide sequenceNumber");
            return "error authHash missing seqNr:0";
        }
        
        if (authHash == null) {
            log.info("Check command does not provide authHash");
            return "error authHash missing seqNr:" + String.valueOf(++this.seqNr);
        }
        if (zoneId == -1) {
            log.info("Check command does not provide zone");
            return "error zone id missing seqNr:" + String.valueOf(++this.seqNr);
        }
        if (terminalId == -1) {
            log.info("Check command does not provide terminal");
            return "error terminal id missing seqNr:" + String.valueOf(++this.seqNr);
        }
        
        if (pin != -1 && bioHash != null) {
            return authService.checkAuth(authHash, zoneId, terminalId, pin, bioHash) + " seqNr:" + String.valueOf(++this.seqNr);
        }
        if (pin != -1) {
            return authService.checkAuth(authHash, zoneId, terminalId, pin) + " seqNr:" + String.valueOf(++this.seqNr);
        }
        if (bioHash != null) {
            return authService.checkAuth(authHash, zoneId, terminalId, bioHash) + " seqNr:" + String.valueOf(++this.seqNr);
        }
        
        return authService.checkAuth(authHash, zoneId, terminalId) + " seqNr:" + String.valueOf(++this.seqNr);
    }
    
    private String logCmd(String cmd) {
        log.info("Received logEvent command");
        String[] c = cmd.split(" ");
        if (c.length < 6) {
            log.info("LogEvent command has insufficient argument number");
            return "error missing arguments";
        }
        
        int objectId = -1;
        int terminalId = -1;
        boolean notify = false;
        String recipient = null;
        String channel = null;
        String message = null;
        int cSeqNr = this.seqNr;
        
        for (int i=1; i<c.length; i++) {
            String[] pair = c[i].split(":");
            if (pair.length != 2) {
                log.info("LogEvent command has invalid syntax {}", c[i]);
                return "error argument syntax seqNr:" + String.valueOf(++this.seqNr);
            }
            switch(pair[0]) {
                case "object":
                    try {
                        objectId = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("LogEvent command has invalid object {}", pair[1]);
                        return "error invalid object seqNr:" + String.valueOf(++this.seqNr);
                    }
                    break;
                case "terminal":
                    try {
                        terminalId = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("LogEvent command has invalid terminal {}", pair[1]);
                        return "error invalid terminal seqNr:" + String.valueOf(++this.seqNr);
                    }
                    break;
                case "notify":
                    notify = Boolean.parseBoolean(pair[1]);
                    break;
                case "recipient":
                    recipient = pair[1];
                    break;
                case "channel":
                    channel = pair[1];
                    break;
                case "message":
                    message = pair[1];
                    break;
                case "seqNr":
                    try {
                        cSeqNr = Integer.parseInt(pair[1]);
                    } catch (NumberFormatException ex) {
                        log.info("Check command has invalid seqNr {}", pair[1]);
                        loggingService.addLogEntryAndNotify(0, 0, "PCP received invalid sequence number", "technical staff", "message");
                        return "error invalid sequenceNumber seqNr:0";
                    }
                    if (this.seqNr == -1) {
                        this.seqNr = cSeqNr;
                    } else if (cSeqNr <= this.seqNr) {
                        log.info("Check command has invalid seqNr {}", pair[1]);
                        loggingService.addLogEntryAndNotify(0, 0, "PCP received invalid sequence number", "technical staff", "message");
                        return "error invalid sequenceNumber seqNr:0";
                    }
                    break;
                default:
                    log.info("LogEvent command has invalid argument {}", pair[0]);
                    return "error invalid argument seqNr:0";
            }
        }
        
        if (notify && (recipient == null || channel == null)) {
            log.info("LogEvent command sets notification without specifying recipient or channel");
            return "error invalid notification argument seqNr:" + String.valueOf(++this.seqNr);
        }
        if (!notify && (recipient != null || channel != null)) {
            log.info("LogEvent command specifying recipient or channel but notification is false");
            return "error invalid notification argument seqNr:" + String.valueOf(++this.seqNr);
        }
        if (message == null) {
            log.info("LogEvent command does not specify message");
            return "error missing message argument seqNr:" + String.valueOf(++this.seqNr);
        }
        if (objectId == -1) {
            log.info("LogEvent command does not specify object id");
            return "error missing object id seqNr:" + String.valueOf(++this.seqNr);
        }
        if (terminalId == -1) {
            log.info("LogEvent command does not specify terminal");
            return "error missing terminal id seqNr:" + String.valueOf(++this.seqNr);
        }
        
        if (notify) {
            return loggingService.addLogEntryAndNotify(objectId, terminalId, message, recipient, channel) + " seqNr:" + String.valueOf(++this.seqNr);
        }
        return loggingService.addLogEntry(objectId, terminalId, message) + " seqNr:" + String.valueOf(++this.seqNr);
    }
}
